///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file timing.cpp
///
/// \brief   implementation of functions and classes used in timing the code
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <map>

#include <boost/format.hpp>

//#include <src/pcps.h>
#include <src/timing.h>
#include <src/exception.h>
#include <src/mpi_interface.h>

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   constructs a new stopwatch
///
///////////////////////////////////////////////////////////////////////////////////////////////////
pcps::Stopwatch::Stopwatch() : _running(false), _start_time(0), _elapsed_time(0) {}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   resets the stopwatch
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Stopwatch::reset() {
  if (_running)
    throw pcps::Exception("cannot reset Stopwatch because it is running");
  _elapsed_time = 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   starts the stopwatch
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Stopwatch::start() {
  if (_running)
    throw pcps::Exception("cannot start Stopwatch because it is already running");
  _start_time = MPI::Wtime();
  _running = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   stops the stopwatch
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Stopwatch::stop() {
  if (!_running)
    throw pcps::Exception("cannot stop Stopwatch because it is not running");
  _elapsed_time = _elapsed_time + ( MPI::Wtime() - _start_time );
  _running = false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   returns the elapsed time in seconds
///
///////////////////////////////////////////////////////////////////////////////////////////////////
double pcps::Stopwatch::elapsed_seconds() const {
  double total = _elapsed_time;
  if (_running)
    total = total + ( MPI::Wtime() - _start_time );
  return total;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   container to hold timers in use by the pcps code
///
///////////////////////////////////////////////////////////////////////////////////////////////////
static std::map<std::string, pcps::Stopwatch> pcps_timer_container;

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   prints all timers in pcps_timer_container
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::print_timers() {
  std::cout << std::endl;
  std::cout << boost::format("Printing all timers used during this execution:") << std::endl;
  std::map<std::string, pcps::Stopwatch>::const_iterator it;
  for (it = pcps_timer_container.begin(); it != pcps_timer_container.end(); it++)
    std::cout << boost::format("  %-30s      %15.6f seconds") % it->first % it->second.elapsed_seconds() << std::endl;
  std::cout << std::endl;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   starts a timer
///
/// \param[in]     name     a string identifying the timer
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::start_timer(const std::string & name) {
  pcps_timer_container[name].start();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   stops a timer
///
/// \param[in]     name     a string identifying the timer
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::stop_timer(const std::string & name) {
  pcps_timer_container[name].stop();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   resets a timer
///
/// \param[in]     name     a string identifying the timer
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::reset_timer(const std::string & name) {
  pcps_timer_container[name].reset();
}

